package com.qiangesoft.bootcodegen.service.impl;

import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qiangesoft.bootcodegen.constant.CodegenConstant;
import com.qiangesoft.bootcodegen.constant.DbType;
import com.qiangesoft.bootcodegen.constant.QueryType;
import com.qiangesoft.bootcodegen.entity.BcgDataSource;
import com.qiangesoft.bootcodegen.entity.BcgTable;
import com.qiangesoft.bootcodegen.entity.BcgTableColumn;
import com.qiangesoft.bootcodegen.framework.config.CodeGenParam;
import com.qiangesoft.bootcodegen.mapper.BcgTableMapper;
import com.qiangesoft.bootcodegen.mapper.DbMapper;
import com.qiangesoft.bootcodegen.pojo.bo.BcgTableBO;
import com.qiangesoft.bootcodegen.pojo.bo.BcgTableColumnBO;
import com.qiangesoft.bootcodegen.pojo.convert.BcgTableColumnConvert;
import com.qiangesoft.bootcodegen.pojo.convert.BcgTableConvert;
import com.qiangesoft.bootcodegen.pojo.dto.BcgTableColumnConfigDTO;
import com.qiangesoft.bootcodegen.pojo.dto.BcgTableDTO;
import com.qiangesoft.bootcodegen.pojo.dto.BcgTableImportDTO;
import com.qiangesoft.bootcodegen.pojo.query.BcgTableQuery;
import com.qiangesoft.bootcodegen.pojo.query.PageQuery;
import com.qiangesoft.bootcodegen.pojo.vo.BcgTableDetailVO;
import com.qiangesoft.bootcodegen.pojo.vo.BcgTableVO;
import com.qiangesoft.bootcodegen.pojo.vo.PageResultVO;
import com.qiangesoft.bootcodegen.service.IBcgDataSourceService;
import com.qiangesoft.bootcodegen.service.IBcgTableColumnService;
import com.qiangesoft.bootcodegen.service.IBcgTableService;
import com.qiangesoft.bootcodegen.utils.CodeGenUtil;
import com.qiangesoft.bootcodegen.utils.TableColumnInitUtil;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 表配置 服务实现类
 * </p>
 *
 * @author qiangesoft
 * @date 2024-03-28
 */
@RequiredArgsConstructor
@Service
public class BcgTableServiceImpl extends ServiceImpl<BcgTableMapper, BcgTable> implements IBcgTableService {

    private final IBcgDataSourceService dataSourceService;

    private final IBcgTableColumnService tableColumnService;

    private final DbMapper dbMapper;

    @Override
    public void saveTable(BcgTableImportDTO tableImportDTO) {
        List<BcgTableBO> tableBOList;
        List<BcgTableColumnBO> tableColumnBOList;

        String dbType = DbType.MySQL.name();
        Long dataSourceId = tableImportDTO.getDataSourceId();
        try {
            // 先切换数据源把数据库表信息拿出来
            if (dataSourceId != null && !CodegenConstant.MASTER_DATA_SOURCE_ID.equals(dataSourceId)) {
                BcgDataSource bcgDataSource = dataSourceService.getById(dataSourceId);
                if (bcgDataSource != null) {
                    dbType = bcgDataSource.getDbType();
                    DynamicDataSourceContextHolder.push(String.valueOf(dataSourceId));
                }
            }
            // 表
            List<BcgTableBO> allTableList = dbMapper.listAllTable(dbType);
            tableBOList = allTableList.stream().filter(e -> tableImportDTO.getTables().contains(e.getTableName())).collect(Collectors.toList());
            List<String> tableNames = tableBOList.stream().map(BcgTableBO::getTableName).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(tableNames)) {
                return;
            }

            // 表字段
            tableColumnBOList = dbMapper.listTableColumn(dbType, tableNames);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("导入表失败");
        } finally {
            DynamicDataSourceContextHolder.poll();
        }

        // 入库
        for (BcgTableBO tableBO : tableBOList) {
            BcgTable table = TableColumnInitUtil.initTable(tableBO);
            table.setDataSourceId(dataSourceId);
            baseMapper.insert(table);

            List<BcgTableColumnBO> columnBOList = tableColumnBOList.stream().filter(e -> table.getTableName().equals(e.getTableName())).collect(Collectors.toList());
            List<BcgTableColumn> tableColumnList = TableColumnInitUtil.initTableColumn(table.getId(), dbType, columnBOList);
            tableColumnService.saveBatch(tableColumnList);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void removeTable(List<Long> ids) {
        baseMapper.deleteBatchIds(ids);

        LambdaQueryWrapper<BcgTableColumn> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(BcgTableColumn::getTableId, ids);
        tableColumnService.remove(queryWrapper);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateTable(Long id, BcgTableDTO tableDTO) {
        BcgTable bcgTable = BcgTableConvert.INSTANCE.configDtoToEntity(tableDTO.getTableConfig());
        bcgTable.setId(id);
        baseMapper.updateById(bcgTable);

        List<BcgTableColumnConfigDTO> tableColumnConfigList = tableDTO.getTableColumnConfigList();
        List<BcgTableColumn> tableColumnList = tableColumnConfigList.stream().map(BcgTableColumnConvert.INSTANCE::dtoToEntity).collect(Collectors.toList());
        for (BcgTableColumn tableColumn : tableColumnList) {
            if (!tableColumn.getPageQuery()) {
                tableColumn.setPageQueryType(null);
            } else if (tableColumn.getPageQuery() && ObjectUtils.isEmpty(tableColumn.getPageQueryType())) {
                tableColumn.setPageQueryType(QueryType.EQ.getValue());
            }
        }
        tableColumnService.updateBatchById(tableColumnList);
    }

    @Override
    public PageResultVO<BcgTableVO> listTable(PageQuery pageQuery, BcgTableQuery tableQuery) {
        long pageNum = pageQuery.getPageNum();
        long pageSize = pageQuery.getPageSize();

        PageResultVO<BcgTableVO> pageResult = new PageResultVO<>();
        pageResult.setPageNum(pageNum);
        pageResult.setPageSize(pageSize);

        LambdaQueryWrapper<BcgTable> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper
                .eq(tableQuery.getDataSourceId() != null, BcgTable::getDataSourceId, tableQuery.getDataSourceId())
                .like(StringUtils.isNotBlank(tableQuery.getTableName()), BcgTable::getTableName, tableQuery.getTableName())
                .orderByDesc(BcgTable::getCreateTime);
        Page<BcgTable> page = baseMapper.selectPage(new Page<>(pageNum, pageSize), queryWrapper);
        pageResult.setTotal(page.getTotal());
        pageResult.setPages(page.getPages());

        List<BcgTable> records = page.getRecords();
        List<Long> dataSourceIdList = records.stream().map(BcgTable::getDataSourceId).filter(Objects::nonNull).collect(Collectors.toList());
        Map<Long, String> dataSourceMap = new HashMap<>();
        dataSourceMap.put(CodegenConstant.MASTER_DATA_SOURCE_ID, CodegenConstant.MASTER_DATA_SOURCE_NAME);
        if (!CollectionUtils.isEmpty(dataSourceIdList)) {
            dataSourceService.listByIds(dataSourceIdList).forEach(e -> dataSourceMap.put(e.getId(), e.getName()));
        }
        List<BcgTableVO> voList = new ArrayList<>();
        for (BcgTable record : records) {
            BcgTableVO vo = BcgTableConvert.INSTANCE.entityToVo(record);
            vo.setDataSourceName(dataSourceMap.get(record.getDataSourceId()));
            voList.add(vo);
        }
        pageResult.setResults(voList);
        return pageResult;
    }

    @Override
    public BcgTableDetailVO getTable(Long id) {
        BcgTable bcgTable = baseMapper.selectById(id);
        if (bcgTable == null) {
            return null;
        }
        BcgTableDetailVO detailVO = BcgTableConvert.INSTANCE.entityToDetailVo(bcgTable);
        Long dataSourceId = bcgTable.getDataSourceId();
        detailVO.setDataSourceName(CodegenConstant.MASTER_DATA_SOURCE_NAME);
        if (!CodegenConstant.MASTER_DATA_SOURCE_ID.equals(dataSourceId)) {
            BcgDataSource dataSource = dataSourceService.getById(dataSourceId);
            detailVO.setDataSourceName(dataSource.getName());
        }
        return detailVO;
    }

    @Override
    public void codegenDownload(Long id, HttpServletResponse response) {
        BcgTable table = baseMapper.selectById(id);

        CodeGenParam codeGenParam = new CodeGenParam();
        codeGenParam.setTableName(table.getTableName());
        codeGenParam.setGenModule(table.getGenModule());
        codeGenParam.setGenFunction(table.getGenFunction());
        codeGenParam.setGenAuthor(table.getGenAuthor());
        codeGenParam.setGenDate(new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
        codeGenParam.setGenPackage(table.getGenPackage());
        codeGenParam.setGenClass(table.getGenClass());

        LambdaQueryWrapper<BcgTableColumn> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BcgTableColumn::getTableId, id);
        List<BcgTableColumn> tableColumnList = tableColumnService.list(queryWrapper);
        codeGenParam.setColumnList(tableColumnList);

        CodeGenUtil.genDownload(codeGenParam, response);
    }

    @Override
    public void codegenLocal(Long id) {
        BcgTable table = baseMapper.selectById(id);

        CodeGenParam codeGenParam = new CodeGenParam();
        codeGenParam.setTableName(table.getTableName());
        codeGenParam.setGenModule(table.getGenModule());
        codeGenParam.setGenPath(table.getGenPath());
        codeGenParam.setGenFunction(table.getGenFunction());
        codeGenParam.setGenAuthor(table.getGenAuthor());
        codeGenParam.setGenDate(new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
        codeGenParam.setGenPackage(table.getGenPackage());
        codeGenParam.setGenClass(table.getGenClass());

        LambdaQueryWrapper<BcgTableColumn> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(BcgTableColumn::getTableId, id);
        List<BcgTableColumn> tableColumnList = tableColumnService.list(queryWrapper);
        codeGenParam.setColumnList(tableColumnList);
        try {
            CodeGenUtil.genLocal(codeGenParam);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
